﻿// oooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Mogre;
using MogreNewt;
using System.IO;


using System.Runtime.InteropServices;
using System.Threading;
using Win32;
using System.Xml.Serialization;

namespace MogreMicroscope
{

    public struct VertsWrapper
    {
        public Vector3[] vertices;
        public uint vertex_count;
        public AxisAlignedBox AAB;
        public Quaternion Rotation;

        public VertsWrapper(Vector3[] vertArr, uint cnt)
        {
            vertex_count = cnt;
            vertices = vertArr;
            AAB = new AxisAlignedBox();
            Rotation = Quaternion.ZERO;
        }
    }

    public class MeshUtils
    {
        #region MESH_INFO
        /// <summary>
        /// Get the mesh information for the given mesh. 
        /// Written by barkas from Mogre forum
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="vertex_count"></param>
        /// <param name="vertices"></param>
        /// <param name="index_count"></param>
        /// <param name="indices"></param>
        /// <param name="transform"></param>
        public static unsafe void GetMeshInformation(MeshPtr mesh,
            ref uint vertex_count,
            ref Vector3[] vertices,
            ref uint index_count,
            ref UInt64[] indices,
            Matrix4 transform)
        {
            bool added_shared = false;
            uint current_offset = 0;
            uint shared_offset = 0;
            uint next_offset = 0;
            uint index_offset = 0;

            vertex_count = index_count = 0;

            for (ushort i = 0; i < mesh.NumSubMeshes; ++i)
            {
                SubMesh submesh = mesh.GetSubMesh(i);
                if (submesh.useSharedVertices)
                {
                    if (!added_shared)
                    {
                        vertex_count += mesh.sharedVertexData.vertexCount;
                        added_shared = true;
                    }
                }
                else
                {
                    vertex_count += submesh.vertexData.vertexCount;
                }

                index_count += submesh.indexData.indexCount;
            }

            vertices = new Vector3[vertex_count];
            indices = new UInt64[index_count];
            added_shared = false;

            for (ushort i = 0; i < mesh.NumSubMeshes; ++i)
            {
                SubMesh submesh = mesh.GetSubMesh(i);
                VertexData vertex_data = submesh.useSharedVertices ? mesh.sharedVertexData : submesh.vertexData;

                if (!submesh.useSharedVertices || (submesh.useSharedVertices && !added_shared))
                {
                    if (submesh.useSharedVertices)
                    {
                        added_shared = true;
                        shared_offset = current_offset;
                    }

                    VertexElement posElem =
                        vertex_data.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.VES_POSITION);
                    HardwareVertexBufferSharedPtr vbuf =
                        vertex_data.vertexBufferBinding.GetBuffer(posElem.Source);

                    byte* vertex = (byte*)vbuf.Lock(HardwareBuffer.LockOptions.HBL_READ_ONLY);
                    float* pReal;

                    for (int j = 0; j < vertex_data.vertexCount; ++j, vertex += vbuf.VertexSize)
                    {
                        posElem.BaseVertexPointerToElement(vertex, &pReal);
                        Vector3 pt = new Vector3(pReal[0], pReal[1], pReal[2]);
                        vertices[current_offset + j] = transform * pt;
                    }
                    vbuf.Unlock();
                    vbuf.Dispose();
                    next_offset += vertex_data.vertexCount;
                }

                IndexData index_data = submesh.indexData;
                uint numTris = index_data.indexCount / 3;
                HardwareIndexBufferSharedPtr ibuf = index_data.indexBuffer;

                bool use32bitindexes = (ibuf.Type == HardwareIndexBuffer.IndexType.IT_32BIT);

                UInt32* pLong = (UInt32*)ibuf.Lock(HardwareBuffer.LockOptions.HBL_READ_ONLY);
                ushort* pShort = (ushort*)pLong;
                uint offset = submesh.useSharedVertices ? shared_offset : current_offset;
                if (use32bitindexes)
                {
                    for (int k = 0; k < index_data.indexCount; ++k)
                    {
                        indices[index_offset++] = (UInt64)pLong[k] + (UInt64)offset;
                    }
                }
                else
                {
                    for (int k = 0; k < index_data.indexCount; ++k)
                    {
                        indices[index_offset++] = (UInt64)pShort[k] + (UInt64)offset;
                    }
                }
                ibuf.Unlock();
                ibuf.Dispose();
                current_offset = next_offset;
            }

        }

        


        /// <summary>
        /// 
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="scale"></param>
        /// <returns></returns>
        public static unsafe List<VertsWrapper> GetMeshVerticesAsBoxes(MeshPtr mesh, Vector3 scale)
        {
            bool added_shared = false;
            uint current_offset = 0;
            uint shared_offset = 0;
            uint next_offset = 0;


            List<VertsWrapper> vertBlocks = new List<VertsWrapper>();


            uint vertex_count = 0;

            added_shared = false;

            for (ushort i = 0; i < mesh.NumSubMeshes; ++i)
            {
                SubMesh submesh = mesh.GetSubMesh(i);



                if (submesh.useSharedVertices)
                {
                    vertex_count = mesh.sharedVertexData.vertexCount;
                }
                else
                {
                    vertex_count = submesh.vertexData.vertexCount;
                }
                //VertsWrapper vertBlock = new VertsWrapper(new Vector3[vertex_count], vertex_count);
                VertsWrapper vertBlock = new VertsWrapper(new Vector3[8], 8);

                VertexData vertex_data = submesh.useSharedVertices ? mesh.sharedVertexData : submesh.vertexData;

                if (!submesh.useSharedVertices || (submesh.useSharedVertices && !added_shared))
                {
                    if (submesh.useSharedVertices)
                    {
                        added_shared = true;
                        shared_offset = current_offset;
                    }

                    VertexElement posElem = vertex_data.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.VES_POSITION);
                    HardwareVertexBufferSharedPtr vbuf = vertex_data.vertexBufferBinding.GetBuffer(posElem.Source);

                    byte* vertex = (byte*)vbuf.Lock(HardwareBuffer.LockOptions.HBL_READ_ONLY);
                    float* pReal;

                    Vector3 min = Vector3.ZERO;
                    Vector3 max = Vector3.ZERO;

                    for (int j = 0; j < vertex_data.vertexCount; ++j, vertex += vbuf.VertexSize)
                    {
                        posElem.BaseVertexPointerToElement(vertex, &pReal);
                        Vector3 pt = new Vector3(pReal[0], pReal[1], pReal[2]);
                        pt *= scale;

                        // Find min, max xyz for this mesh - assumes will be centred on 0,0,0 as BB is initialised to 0,0,0

                        // Console.WriteLine("" + pt);
                        if (j == 0)
                        {
                            min = max = pt;
                        }

                        min.MakeFloor(pt);
                        max.MakeCeil(pt);
                    }

                    vertBlock.AAB = new AxisAlignedBox(min, max);

                    vertBlock.vertices = vertBlock.AAB.GetAllCorners();

                    vbuf.Unlock();
                    vbuf.Dispose();
                    next_offset += vertex_data.vertexCount;


                    vertBlocks.Add(vertBlock);
                }

                current_offset = next_offset;
            }
            return vertBlocks;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="mesh"></param>
        /// <param name="scale"></param>
        /// <returns></returns>
        public static unsafe List<VertsWrapper> GetMeshVertices(MeshPtr mesh, Vector3 scale)
        {
            bool added_shared = false;
            uint current_offset = 0;
            uint shared_offset = 0;
            uint next_offset = 0;
            //uint index_offset = 0;

            List<VertsWrapper> vertBlocks = new List<VertsWrapper>();


            uint vertex_count = 0;

            added_shared = false;

            for (ushort i = 0; i < mesh.NumSubMeshes; ++i)
            {
                SubMesh submesh = mesh.GetSubMesh(i);



                if (submesh.useSharedVertices)
                {
                    vertex_count = mesh.sharedVertexData.vertexCount;
                }
                else
                {
                    vertex_count = submesh.vertexData.vertexCount;
                }
                VertsWrapper vertBlock = new VertsWrapper(new Vector3[vertex_count], vertex_count);


                VertexData vertex_data = submesh.useSharedVertices ? mesh.sharedVertexData : submesh.vertexData;

                if (!submesh.useSharedVertices || (submesh.useSharedVertices && !added_shared))
                {
                    if (submesh.useSharedVertices)
                    {
                        added_shared = true;
                        shared_offset = current_offset;
                    }

                    VertexElement posElem = vertex_data.vertexDeclaration.FindElementBySemantic(VertexElementSemantic.VES_POSITION);
                    HardwareVertexBufferSharedPtr vbuf = vertex_data.vertexBufferBinding.GetBuffer(posElem.Source);

                    byte* vertex = (byte*)vbuf.Lock(HardwareBuffer.LockOptions.HBL_READ_ONLY);
                    float* pReal;

                    Vector3 min = Vector3.ZERO;
                    Vector3 max = Vector3.ZERO;
                    for (int j = 0; j < vertex_data.vertexCount; ++j, vertex += vbuf.VertexSize)
                    {
                        posElem.BaseVertexPointerToElement(vertex, &pReal);
                        Vector3 pt = new Vector3(pReal[0], pReal[1], pReal[2]);
                        pt *= scale;

                        // Find min, max xyz for this mesh - assumes will be centred on 0,0,0 as BB is initialised to 0,0,0
                        if (j == 0)
                        {
                            min = max = pt;
                        }
                        min.MakeFloor(pt);
                        max.MakeCeil(pt);

                        vertBlock.vertices[/*current_offset + */j] = /*transform * */pt;
                    }

                    vertBlock.AAB = new AxisAlignedBox(min, max);
                    vbuf.Unlock();
                    vbuf.Dispose();
                    next_offset += vertex_data.vertexCount;
                    vertBlocks.Add(vertBlock);
                }

                current_offset = next_offset;
            }
            return vertBlocks;
        }

        #endregion MESH_INFO
    }
}
